package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import com.atguigu.tingshu.Factory.StrategyFactory;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.strategy.ItemTypeStrategy;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.LocalDateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;


import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;
    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;
    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;
    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;
    @Autowired
    private UserVipServiceMapper userVipServiceMapper;
    @Autowired
    private StrategyFactory strategyFactory;


    @Override
    public void updateVipExpireStatus() {
        Long userId = AuthContextHolder.getUserId();


        userInfoMapper.updateVipExpireStatus();
    }

    /**
     * 保存付费记录
     * @param userPaidRecordVo
     */

    @Override
    @Transactional( rollbackFor = Exception.class)
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        ItemTypeStrategy strategy = strategyFactory.getStrategy(userPaidRecordVo.getItemType());
        strategy.savePaidRecord(userPaidRecordVo);

    }

    @Override
    public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getAlbumId, albumId).eq(UserPaidTrack::getUserId, userId));
        List<Long> trackIdList = userPaidTrackList.stream().map(UserPaidTrack::getAlbumId).collect(Collectors.toList());

        return trackIdList;
    }

    /**
     * 判断用户是否购买了专辑
     * @param userId
     * @param albumId
     * @return
     */

    @Override
    public Boolean isPaidAlbum(Long userId, Long albumId) {

        return userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getAlbumId,albumId).eq(UserPaidAlbum::getUserId,userId)) !=null;
    }

    /**
     * 根据专辑ID和用户ID获取付费曲目ID列表的状态
     * 此方法用于检查哪些曲目在给定的专辑和用户付费曲目列表中已付费
     *
     * @param albumId 专辑的ID
     * @param paidTrackIdList 用户付费的曲目ID列表
     * @param userId 用户的ID
     * @return 返回一个映射，键为曲目ID，值为该曲目是否已付费（1表示已付费，0表示未付费）
     */
    @Override
    public Map<Long, Integer> getUserPaidTrackIdList(Long albumId, List<Long> paidTrackIdList, Long userId) {
        // 检查用户是否购买了整个专辑
        UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>()
                .eq(UserPaidAlbum::getAlbumId, albumId)
                .eq(UserPaidAlbum::getUserId, userId));
        // 如果用户购买了整个专辑，则认为所有曲目都已付费
        if (null != userPaidAlbum) {
            Map<Long, Integer> map1 = paidTrackIdList.stream().collect(Collectors.toMap(trackId -> trackId, k -> 0));
            return map1;
        }

        // 查询用户在特定专辑中付费的曲目列表
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>()
                .eq(UserPaidTrack::getUserId, userId)
                .eq(UserPaidTrack::getAlbumId, albumId)
                .in(UserPaidTrack::getTrackId, paidTrackIdList));
        // 如果付费曲目列表为空，则返回所有曲目都未付费的映射
        if (CollectionUtils.isEmpty(userPaidTrackList)) {
            return paidTrackIdList.stream().collect(Collectors.toMap(trackId -> trackId, k -> 0));
        } else {
            // 创建一个映射来存储每个曲目的付费状态
            Map<Long, Integer> map = new HashMap<>();
            for (Long trackId : paidTrackIdList) {
                // 如果曲目在付费曲目列表中，则标记为已付费，否则标记为未付费
                if (userPaidTrackList.contains(trackId)) {
                    map.put(trackId, 1);
                } else {
                    map.put(trackId, 0);
                }
            }
            return map;
        }
    }



    /**
     * 更新用户信息
     *
     * 此方法用于根据提供的用户ID更新用户的个人信息它首先将传入的UserInfoVo对象中的属性
     * 复制到一个新的UserInfo对象中，然后设置该对象的ID为提供的userId，最后调用userInfoMapper
     * 的updateById方法来更新数据库中的相应记录
     *
     * @param userInfoVo 包含待更新用户信息的VO对象
     * @param userId 用户的ID，用于定位数据库中的用户记录
     */
    @Override
    public void updateUserInfo(UserInfoVo userInfoVo, Long userId) {
        // 创建一个新的UserInfo对象，用于存储将被更新的用户信息
        UserInfo userInfo = new UserInfo();
        // 将userInfoVo对象的属性复制到userInfo对象中
        BeanUtils.copyProperties(userInfoVo, userInfo);
        // 设置userInfo对象的ID为提供的userId
        userInfo.setId(userId);
        // 调用userInfoMapper的updateById方法更新数据库中的用户信息
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 根据用户ID获取用户信息的视图对象
     *
     * @param userId 用户ID，用于标识哪个用户
     * @return 返回用户信息的视图对象（UserInfoVo）
     *
     * 该方法首先通过用户的ID获取用户信息实体（UserInfo），
     * 然后将用户信息实体的数据复制到用户信息视图对象（UserInfoVo）中，
     * 最后返回这个视图对象供其他部分使用
     */
    @Override
    public UserInfoVo getUserInfo(Long userId) {
        // 通过用户ID获取用户信息实体
        UserInfo userInfo = this.getById(userId);

        // 创建用户信息视图对象
        UserInfoVo userInfoVo = new UserInfoVo();

        // 将用户信息实体的数据复制到视图对象中
        BeanUtils.copyProperties(userInfo, userInfoVo);

        // 返回用户信息的视图对象
        return userInfoVo;
    }

    /**
     * 处理微信登录请求
     * 通过微信提供的code换取session信息，从而获取用户的openid
     * 如果该openid对应用户不存在，则创建新用户
     * 最后生成登录令牌并存储用户信息于Redis中
     *
     * @param code 微信授权码
     * @return 包含登录令牌的Map
     */
    @Override
    public Map<String, Object> wxLogin(String code) {
        String openid = null;
        try {
            // 通过微信提供的code换取openid
            openid = wxMaService.jsCode2SessionInfo(code).getOpenid();
        } catch (Exception e) {
            // 捕获异常并重新抛出为运行时异常
            throw new RuntimeException(e);
        }
        // 根据openid查询数据库，获取用户信息
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getWxOpenId, openid));
        if (null == userInfo) {
            // 如果用户不存在，则创建新用户
            userInfo = new UserInfo();
            userInfo.setWxOpenId(openid);
            userInfo.setNickname("听友：" + System.currentTimeMillis());
            userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
            this.userInfoMapper.insert(userInfo);
            // 发送消息到消息队列，用于用户注册相关业务
            this.rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, userInfo.getId());
        }
        // 生成随机登录令牌
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        // 将用户信息存储到Redis中，设置过期时间
        this.redisTemplate.opsForValue().set(userLoginKey, userInfo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
        // 创建返回Map，放入登录令牌
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        return map;
    }


}
